from django.http import HttpResponse, FileResponse, HttpResponseRedirect
from django.urls import reverse
from django.shortcuts import get_object_or_404, redirect, render
from .models import StudentInfo, TeacherInfo, ClassCourse, ClassInfo, CourseInfo
from django.contrib import auth
from django.contrib.auth.models import User, Group
from django.contrib.auth.decorators import user_passes_test
from django.db import transaction, IntegrityError, DatabaseError
import os, re
import pandas as pd
from django.contrib.auth.decorators import login_required
from zy_school.settings import BASE_DIR
import urllib.parse
from urllib.parse import urlencode
import chardet
from .forms import TeacherForm, StudentForm


# 判断是用户是否有效且是否在用户组group_name
def is_in_group(user, group_name):
    if not user.is_active:
        return False
    return user.groups.filter(name=group_name).exists()


# 身份证验证
def id_card_verify(id_card):
    id_card_pattern = re.compile(
        r"^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}([0-9]|X)$"
    )
    return bool(id_card_pattern.match(id_card))


# 手机号验证
def tel_verify(tel):
    tel_pattern = re.compile(r"^1[3-9]\d{9}$")
    return bool(tel_pattern.match(tel))


def login(request):
    """
    GET请求，跳转登录页面
    POST请求，登录验证
    """
    if request.method == "GET" and not request.user.is_authenticated:
        # GET请求且未登录
        return render(request, "manage/login.html")
    elif request.method == "POST" and not request.user.is_authenticated:
        # POST请求且未登录
        username = request.POST.get("username")
        password = request.POST.get("password")
        resUser: User = auth.authenticate(username=username, password=password)
        if not resUser and (
            StudentInfo.objects.filter(student_id=username).exists()
            or TeacherInfo.objects.filter(teacher_id=username).exists()
        ):
            return redirect("manage:register")
        elif not resUser:
            context = {
                "error": "用户名或密码错误！",
                "username": username,
                "password": password,
            }
            return render(request, "manage/login.html", context=context)
        elif resUser and resUser.is_active:
            auth.login(request, resUser)
            return redirect("manage:index")
    elif request.user.is_authenticated and request.user.is_active:
        # 已登录且账户有效
        return redirect("manage:index")


def logout(request):
    auth.logout(request)
    return render(request, "manage/login.html")


def register(request):
    """
    注册页面
    """
    if request.method == "GET":
        return render(request, "manage/register.html")
    elif request.method == "POST":
        # 注册
        username = request.POST.get("username")
        first_name = request.POST.get("first_name")
        id_card = request.POST.get("id_card")
        password = request.POST.get("password")
        confirm_password = request.POST.get("confirm_password")
        tel = request.POST.get("tel")
        if len(id_card) != 18:
            context = {
                "username": username,
                "first_name": first_name,
                "id_card": id_card,
                "tel": tel,
                "password": password,
                "info": "身份证号码格式错误！",
            }
            return render(request, "manage/register.html", context=context)
        else:
            if User.objects.filter(username=username).exists():
                context = {
                    "info": "用户名已存在！请直接登录",
                }
                return render(request, "manage/register.html", context=context)
            elif password != confirm_password:
                context = {
                    "username": username,
                    "first_name": first_name,
                    "id_card": id_card,
                    "tel": tel,
                    "password": password,
                    "info": "两次密码输入不一致！",
                }
                return render(request, "manage/register.html", context=context)
            # 判断教师 or 学生
            elif TeacherInfo.objects.filter(
                teacher_id=username, name=first_name
            ).exists():
                try:
                    user = User.objects.create_user(
                        username=username,
                        password=password,
                        first_name=first_name,
                    )
                    user.groups.add(Group.objects.get(name="教师组"))
                except Exception as e:
                    context = {
                        "username": username,
                        "first_name": first_name,
                        "id_card": id_card,
                        "tel": tel,
                        "password": password,
                        "info": "注册失败！<br>" + str(e),
                    }
                    return render(request, "manage/register.html", context=context)
                try:
                    # TeacherInfo.objects.filter(teacher_id=username).update(
                    #     id_card=id_card, tel=tel
                    # )
                    teacher = TeacherInfo.objects.get(teacher_id=username)
                    teacher.id_card = id_card
                    teacher.tel = tel
                    teacher.save()

                    return redirect("manage:login")
                except Exception as e:
                    context = {
                        "info": "用户信息更新失败！请登录后手动更新！<br>" + str(e),
                    }
                    return render(request, "manage/register.html", context=context)
            elif StudentInfo.objects.filter(
                student_id=username, name=first_name
            ).exists():
                try:
                    user = User.objects.create_user(
                        username=username,
                        password=password,
                        first_name=first_name,
                    )
                    user.groups.add(Group.objects.get(name="学生组"))
                except Exception as e:
                    context = {
                        "username": username,
                        "first_name": first_name,
                        "id_card": id_card,
                        "tel": tel,
                        "password": password,
                        "info": "注册失败！<br>" + str(e),
                    }
                    return render(request, "manage/register.html", context=context)
                try:
                    # StudentInfo.objects.filter(student_id=username).update(
                    #     id_card=id_card, tel=tel
                    # )
                    student = StudentInfo.objects.get(student_id=username)
                    student.id_card = id_card
                    student.tel = tel
                    student.save()

                    return redirect("manage:login")
                except Exception as e:
                    context = {
                        "info": "用户信息更新失败！请登录后在设置页面更新！<br>"
                        + str(e),
                    }
                    return render(request, "manage/register.html", context=context)
            else:
                context = {
                    "username": username,
                    "first_name": first_name,
                    "id_card": id_card,
                    "tel": tel,
                    "password": password,
                    "info": "注册失败！未查询到用户信息！",
                }
                return render(request, "manage/register.html", context=context)


@login_required(login_url="manage:login")
def index(request):
    """
    登录后首页页面
    """
    # 判断用户组
    is_student = is_in_group(request.user, "学生组")
    is_admin = is_in_group(request.user, "管理员")
    is_teacher = is_in_group(request.user, "教师组")
    context = {
        "is_student": is_student,
        "is_admin": is_admin,
        "is_teacher": is_teacher,
    }
    return render(request, "manage/index.html", context=context)


@user_passes_test(lambda u: is_in_group(u, "管理员"), login_url="manage:login")
def data_import(request):
    """
    批量导入基础信息页面
    """
    # 判断用户组
    is_student = is_in_group(request.user, "学生组")
    is_admin = is_in_group(request.user, "管理员")
    is_teacher = is_in_group(request.user, "教师组")
    context = {
        "is_student": is_student,
        "is_admin": is_admin,
        "is_teacher": is_teacher,
    }
    return render(request, "manage/import.html", context=context)


@user_passes_test(lambda u: is_in_group(u, "管理员"), login_url="manage:login")
def download_template(request, id):
    """
    批量信息导入模版下载
    """
    if id == "teacher_info":
        file_name = "模版：教师信息新增.csv"
    elif id == "class_info":
        file_name = "模版：班级信息新增.csv"
    elif id == "course_info":
        file_name = "模版：课程信息新增.csv"
    elif id == "class_course_info":
        file_name = "模版：班级课程新增.csv"
    elif id == "student_info":
        file_name = "模版：学生信息新增.csv"
    else:
        return HttpResponse("文件不存在", status=404)
    file_path = os.path.join(BASE_DIR, "download", file_name)
    if not os.path.exists(file_path):
        return HttpResponse("文件不存在", status=404)
    else:
        file = open(file_path, "rb")
        response = FileResponse(file)
        response["Content-Type"] = "application/octet-stream"
        encoded_file_name = urllib.parse.quote(file_name)
        response["Content-Disposition"] = (
            f"attachment; filename*=UTF-8''{encoded_file_name}"
        )
        return response


@user_passes_test(lambda u: is_in_group(u, "管理员"), login_url="manage:login")
def data_add(request):
    """
    批量导入基础信息实现
    """
    myfile = request.FILES.get("myfile", None)
    if not myfile:
        context = {
            "result": "fail",
            "error_info": "未选择文件myfile",
        }
    else:
        # 读取前10个字节，检测编码
        encodeing = chardet.detect(myfile.read(10))["encoding"]
        # 重置文件指针到开始位置，避免影响后续读取，丢失第一列列名
        myfile.seek(0)
        data = pd.read_csv(
            myfile,
            sep=",",
            on_bad_lines="warn",
            encoding=encodeing,
            keep_default_na=False,
        )
        error_data = pd.DataFrame(columns=data.columns.tolist() + ["error_info"])
    # 事务处理
    if request.POST.get("data_info") == "teacher_info":
        # 教师信息新增
        ## 插入测试数据
        try:
            TeacherInfo.objects.update_or_create(
                teacher_id="test_teacher",
                defaults={
                    "name": "test",
                    "id_card": "362422100001011011",
                },
            )
        except Exception as e:
            print("插入默认数据错误信息：", e)
        ## 插入正式数据
        try:
            with transaction.atomic():
                error_num = 0
                error_rows = []
                update_len = 0
                for index, row in data.iterrows():
                    try:
                        if len(str(row["id_card"])) != 18:
                            error_num += 1
                            error_row = row.copy()
                            error_row["error_info"] = "身份证号码格式错误！"
                            error_data = pd.concat(
                                [error_data, error_row.to_frame().T], ignore_index=True
                            )
                            error_rows.append(
                                {
                                    "row": f"第{index+2}行",
                                    "error_info": str(row) + "身份证号码格式错误！",
                                }
                            )
                        else:
                            TeacherInfo.objects.update_or_create(
                                teacher_id=row["teacher_id"],
                                defaults={
                                    "name": row["name"],
                                    "id_card": str(row["id_card"]),
                                },
                            )
                            update_len += 1
                    except (Exception, IntegrityError, DatabaseError) as e:
                        error_num += 1
                        error_row = row.copy()
                        error_row["error_info"] = str(e)
                        error_data = pd.concat(
                            [error_data, error_row.to_frame().T], ignore_index=True
                        )
                        error_rows.append(
                            {
                                "row": f"第{index+2}行",
                                "error_info": str(row) + str(e),
                            }
                        )
                context = {
                    "result": "success",
                    "item": "教师信息",
                    "inserted_count": update_len,
                    "error_rows": error_rows,
                    "error_num": error_num,
                }
        except (Exception, IntegrityError, DatabaseError) as e:
            context = {
                "result": "fail",
                "item": "教师信息",
                "error_info": str(e),
            }
    elif request.POST.get("data_info") == "class_info":
        # 班级信息新增
        ## 插入测试数据
        try:
            ClassInfo.objects.update_or_create(
                class_name="test",
                defaults={
                    "grade": 0,
                    "class_header_id": TeacherInfo.objects.get(name="test"),
                },
            )
        except Exception as e:
            print("插入默认数据错误信息：", e)
        ## 插入正式数据
        try:
            with transaction.atomic():
                error_num = 0
                error_rows = []
                update_len = 0
                for index, row in data.iterrows():
                    try:
                        ClassInfo.objects.update_or_create(
                            class_name=row["class_name"],
                            defaults={
                                "grade": row["grade"],
                                "class_header_id": TeacherInfo.objects.get(name="test"),
                            },
                        )
                        update_len += 1
                    except (Exception, IntegrityError, DatabaseError) as e:
                        error_num += 1
                        error_row = row.copy()
                        error_row["error_info"] = str(e)
                        error_data = pd.concat(
                            [error_data, error_row.to_frame().T], ignore_index=True
                        )
                        error_rows.append(
                            {
                                "row": f"第{index+2}行",
                                "error_info": str(row) + str(e),
                            }
                        )
                context = {
                    "result": "success",
                    "item": "班级信息",
                    "inserted_count": update_len,
                    "error_rows": error_rows,
                    "error_num": error_num,
                }
        except (Exception, IntegrityError, DatabaseError) as e:
            context = {
                "result": "fail",
                "item": "班级信息",
                "error_info": str(e),
            }
    elif request.POST.get("data_info") == "course_info":
        # 课程信息新增
        ## 插入测试数据
        try:
            CourseInfo.objects.update_or_create(
                course_name="test",
                defaults={
                    "credit": 0,
                },
            )
        except Exception as e:
            print("插入默认数据错误信息：", e)
        ## 插入正式数据
        try:
            with transaction.atomic():
                error_num = 0
                error_rows = []
                update_len = 0
                for index, row in data.iterrows():
                    try:
                        CourseInfo.objects.update_or_create(
                            course_name=row["course_name"],
                            defaults={
                                "credit": row["credit"],
                            },
                        )
                        update_len += 1
                    except (Exception, IntegrityError, DatabaseError) as e:
                        error_num += 1
                        error_row = row.copy()
                        error_row["error_info"] = str(e)
                        error_data = pd.concat(
                            [error_data, error_row.to_frame().T], ignore_index=True
                        )
                        error_rows.append(
                            {
                                "row": f"第{index+2}行",
                                "error_info": str(row) + str(e),
                            }
                        )
                context = {
                    "result": "success",
                    "item": "课程信息",
                    "inserted_count": update_len,
                    "error_rows": error_rows,
                    "error_num": error_num,
                }
        except (Exception, IntegrityError, DatabaseError) as e:
            context = {
                "result": "fail",
                "item": "课程信息",
                "error_info": str(e),
            }
    elif request.POST.get("data_info") == "class_course_info":
        # 班级课程新增
        ## 插入测试数据
        try:
            ClassCourse.objects.update_or_create(
                class_name="test",
                course_name="test",
                teacher_name="test",
                defaults={
                    "class_id": ClassInfo.objects.get(class_name="test"),
                    "course_id": CourseInfo.objects.get(course_name="test"),
                    "teacher_id": TeacherInfo.objects.get(name="test"),
                },
            )
        except Exception as e:
            print("插入默认数据错误信息：", e)
        ## 插入正式数据
        try:
            with transaction.atomic():
                error_num = 0
                error_rows = []
                update_len = 0
                for index, row in data.iterrows():
                    try:
                        if row["teacher_name"] == "" or not row["teacher_name"]:
                            teacher_name_ = "test"
                            teacher_id_ = TeacherInfo.objects.get(name="test")
                        else:
                            teacher_name_ = row["teacher_name"]
                            teacher_id_ = TeacherInfo.objects.get(name=teacher_name_)

                        ClassCourse.objects.update_or_create(
                            class_name=row["class_name"],
                            course_name=row["course_name"],
                            defaults={
                                "class_id": ClassInfo.objects.get(
                                    class_name=row["class_name"]
                                ),
                                "course_id": CourseInfo.objects.get(
                                    course_name=row["course_name"]
                                ),
                                "teacher_id": teacher_id_,
                                "teacher_name": teacher_name_,
                            },
                        )
                        update_len += 1
                    except (Exception, IntegrityError, DatabaseError) as e:
                        error_num += 1
                        error_row = row.copy()
                        error_row["error_info"] = str(e)
                        error_data = pd.concat(
                            [error_data, error_row.to_frame().T], ignore_index=True
                        )
                        error_rows.append(
                            {
                                "row": f"第{index+2}行",
                                "error_info": str(row) + str(e),
                            }
                        )
                context = {
                    "result": "success",
                    "item": "班级课程信息",
                    "inserted_count": update_len,
                    "error_rows": error_rows,
                    "error_num": error_num,
                }
        except (Exception, IntegrityError, DatabaseError) as e:
            context = {
                "result": "fail",
                "item": "班级课程信息",
                "error_info": str(e),
                "error_rows": error_rows,
            }
    elif request.POST.get("data_info") == "student_info":
        # 学生信息新增
        ## 插入测试数据
        try:
            StudentInfo.objects.update_or_create(
                student_id="test_student",
                defaults={
                    "name": "test",
                    "gender": "男",
                    "age": 0,
                    "id_card": "362422100001011012",
                    "class_id": ClassInfo.objects.get(class_name="test"),
                },
            )
        except Exception as e:
            print("插入默认数据错误信息：", e)

        ## 插入正式数据
        try:
            with transaction.atomic():
                error_num = 0
                error_rows = []
                update_len = 0
                for index, row in data.iterrows():
                    try:
                        if len(str(row["id_card"])) != 18:
                            error_num += 1
                            error_row = row.copy()
                            error_row["error_info"] = "身份证号码格式错误！"
                            error_data = pd.concat(
                                [error_data, error_row.to_frame().T], ignore_index=True
                            )
                            error_rows.append(
                                {
                                    "row": f"第{index+2}行",
                                    "error_info": str(row) + "身份证号码格式错误！",
                                }
                            )
                        else:
                            StudentInfo.objects.update_or_create(
                                student_id=row["student_id"],
                                defaults={
                                    "name": row["name"],
                                    "gender": row["gender"],
                                    "id_card": str(row["id_card"]),
                                    "class_id": ClassInfo.objects.get(
                                        class_name=row["class_name"]
                                    ),
                                    "class_name": row["class_name"],
                                },
                            )
                            update_len += 1
                    except (Exception, IntegrityError, DatabaseError) as e:
                        error_num += 1
                        error_row = row.copy()
                        error_row["error_info"] = str(e)
                        error_data = pd.concat(
                            [error_data, error_row.to_frame().T], ignore_index=True
                        )
                        error_rows.append(
                            {
                                "row": f"第{index+2}行",
                                "error_info": str(row) + str(e),
                            }
                        )
                context = {
                    "result": "success",
                    "item": "学生信息",
                    "inserted_count": update_len,
                    "error_rows": error_rows,
                    "error_num": error_num,
                }
        except (Exception, IntegrityError, DatabaseError) as e:
            context = {
                "result": "fail",
                "item": "学生信息",
                "error_info": str(e),
            }
    else:
        context = {
            "result": "fail",
            "error_info": "未选择导入数据类型",
        }
    # 错误信息保存
    error_data.to_csv(
        os.path.join(BASE_DIR, "download", "import_error_info.csv"), index=False
    )
    context["is_student"] = is_in_group(request.user, "学生组")
    context["is_admin"] = is_in_group(request.user, "管理员")
    context["is_teacher"] = is_in_group(request.user, "教师组")
    return render(request, "manage/import.html", context=context)


@user_passes_test(lambda u: is_in_group(u, "管理员"), login_url="manage:login")
def error_download(request):
    """
    下载批量导入的错误信息
    """
    file = open(os.path.join(BASE_DIR, "download", "import_error_info.csv"), "rb")
    response = FileResponse(file)
    response["Content-Type"] = "application/octet-stream"
    encoded_file_name = urllib.parse.quote("import_error_info.csv")
    response["Content-Disposition"] = (
        f"attachment; filename*=UTF-8''{encoded_file_name}"
    )
    return response


@login_required(login_url="manage:login")
def settings(request):
    """
    个人设置
    """
    context = {}
    context["is_student"] = is_in_group(request.user, "学生组")
    context["is_admin"] = is_in_group(request.user, "管理员")
    context["is_teacher"] = is_in_group(request.user, "教师组")
    if request.method == "GET":
        try:
            if is_in_group(request.user, "教师组"):
                teacher = get_object_or_404(
                    TeacherInfo, teacher_id=request.user.username
                )
                teacher_form = TeacherForm(instance=teacher)
                context["teacher_form"] = teacher_form
            elif is_in_group(request.user, "学生组"):
                student = get_object_or_404(
                    StudentInfo, student_id=request.user.username
                )
                student_form = StudentForm(instance=student)
                context["student_form"] = student_form
        except:
            context["error_info"] = "未查询到个人信息"
        return render(request, "manage/settings.html", context=context)
    elif request.method == "POST":
        # print(request.POST)
        context = {
            "is_student": is_in_group(request.user, "学生组"),
            "is_admin": is_in_group(request.user, "管理员"),
            "is_teacher": is_in_group(request.user, "教师组"),
        }
        if not id_card_verify(request.POST.get("id_card")):
            context["error_info"] = "身份证号码格式错误！"
            return render(request, "manage/settings.html", context=context)
        if not tel_verify(request.POST.get("tel")):
            context["error_info"] = "手机号码格式错误！"
            return render(request, "manage/settings.html", context=context)
        print(request.POST)
        if request.POST.get("teacher_id"):
            teacher = TeacherInfo.objects.get(teacher_id=request.user.username)
            teacher.name = request.POST.get("name")
            teacher.id_card = request.POST.get("id_card")
            teacher.tel = request.POST.get("tel")
            teacher.save()
            context["success_info"] = "个人信息修改成功！"
            return render(request, "manage/settings.html", context=context)
        elif request.POST.get("student_id"):
            student = StudentInfo.objects.get(student_id=request.user.username)
            student.name = request.POST.get("name")
            student.id_card = request.POST.get("id_card")
            student.tel = request.POST.get("tel")
            student.save()
            context["success_info"] = "个人信息修改成功！"
            return render(request, "manage/settings.html", context=context)


@user_passes_test(lambda u: is_in_group(u, "教师组"), login_url="manage:login")
def eval_statistic(request):
    """
    教师评测统计及评测开关
    """
    context = {
        "is_student": is_in_group(request.user, "学生组"),
        "is_admin": is_in_group(request.user, "管理员"),
        "is_teacher": is_in_group(request.user, "教师组"),
    }
    if request.GET.get("class_id"):
        # 班级内学生评测统计页面
        try:
            student_list = StudentInfo.objects.filter(
                class_id=request.GET.get("class_id")
            ).order_by("-remain")
            context["student_list"] = student_list
        except Exception as e:
            context["error_info"] = "未查询到学生信息"
        return render(request, "manage/eval_statistic.html", context=context)
    else:
        # 管理评测开关页面
        try:
            class_list = ClassInfo.objects.filter(
                class_header_id=request.user.username
            ).order_by("-remain")
            context["class_list"] = class_list
        except Exception as e:
            context["error_info"] = "未查询到班级信息"
        return render(request, "manage/eval_statistic.html", context=context)


@user_passes_test(lambda u: is_in_group(u, "教师组"), login_url="manage:login")
def eval_switch(request):
    """
    评测开关切换
    """
    print(request.GET)
    try:
        class_detail = ClassInfo.objects.get(class_id=request.GET.get("class_id"))
        class_detail.is_eval = not class_detail.is_eval
        class_detail.save()
        return redirect("manage:eval_statistic")
        # params = {"type": "manage"}
        # url = reverse("manage:eval_statistic") + "?" + urlencode(params)
        # return HttpResponseRedirect(url)
    except Exception as e:
        return HttpResponse("错误信息：" + str(e))
